home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume4 / se / part2 < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  53.2 KB

  1. From: Jeff Lee <talcott!seismo!gatech!jeff>
  2. Subject: Georgia Tech 'se' screen editor (Part 2 of 8)
  3. Keywords: Software Tools, Yet Another Screen Editor, Both BSD and USG
  4. Newsgroups: mod.sources
  5. Approved: jpn@panda.UUCP
  6.  
  7. Mod.sources:  Volume 4, Issue 83
  8. Submitted by: Jeff Lee <seismo!gatech!jeff>
  9.  
  10. #! /bin/sh
  11. # This is a shell archive, meaning:
  12. # 1. Remove everything above the #! /bin/sh line.
  13. # 2. Save the resulting text in a file.
  14. # 3. Execute the file with /bin/sh (not csh) to create:
  15. #    main.c
  16. #    edit.c
  17. #    scratch.c
  18. # This archive created: Tue Apr 29 11:01:33 1986
  19. export PATH; PATH=/bin:/usr/bin:$PATH
  20. echo shar: "extracting 'main.c'" '(14853 characters)'
  21. if test -f 'main.c'
  22. then
  23.     echo shar: "will not over-write existing file 'main.c'"
  24. else
  25. cat << \SHAR_EOF > 'main.c'
  26. /*
  27. ** main.c
  28. **
  29. ** main program and lots of other routines
  30. ** for the se screen editor.
  31. */
  32.  
  33. #include "se.h"
  34.  
  35. /* declare global variables */
  36.  
  37. /* Concerning line numbers: */
  38. int Line1;        /* first line number on command */
  39. int Line2;        /* second line number on command */
  40. int Nlines;        /* number of line numbers specified */
  41. int Curln;        /* current line; value of dot */
  42. int Lastln;        /* last line; value of dollar */
  43.  
  44.  
  45. /* Concerning patterns: */
  46. char Pat[MAXPAT] = "";    /* saved pattern */
  47.  
  48.  
  49. /* Concerning the text of lines: */
  50. char Txt[MAXLINE];    /* text of current line */
  51.  
  52.  
  53. /* Concerning file names: */
  54. char Savfil[MAXLINE] = "";    /* remembered file name */
  55.  
  56.  
  57. /* Concerning line descriptors: */
  58. LINEDESC Buf[MAXBUF];
  59. #ifdef OLD_SCRATCH
  60. LINEDESC *Lastbf;    /* last pointer used in Buf */
  61. LINEDESC *Free;        /* head of free list */
  62. #endif
  63. LINEDESC *Line0;    /* head of list of line descriptors */
  64.  
  65.  
  66. /* Concerning the 'undo' command: */
  67. LINEDESC *Limbo;    /* head of limbo list for undo */
  68. int Limcnt;        /* number of lines in limbo list */
  69.  
  70.  
  71. /* Concerning the scratch file: */
  72. filedes Scr;        /* scratch file descriptor */
  73. unsigned Scrend;    /* end of info on scratch file */
  74. char Scrname[MAXLINE];    /* name of scratch file */
  75. int Lost_lines;        /* number of garbage lines in scratch file */
  76.  
  77.  
  78. /* Concerning miscellaneous variables */
  79. int Buffer_changed = NO;/* YES if buffer changed since last write */
  80. int Errcode = ENOERR;    /* cause of most recent error */
  81. int Saverrcode = ENOERR;/* cause of previous error */
  82. int Probation = NO;    /* YES if unsaved buffer can be destroyed */
  83. int Argno;        /* command line argument pointer */
  84. char Last_char_scanned = 0;    /* last char scanned w/ctl-[sl], init illegal  */
  85. #ifdef HARD_TERMS
  86. int Tspeed;        /* terminal speed in characters/second */
  87. #endif
  88. char Peekc = EOS;    /* push a SKIP_RIGHT if adding delimiters */
  89. #ifdef BSD4_2
  90. int Reading = NO;    /* are we doing terminal input? */
  91. #endif
  92.  
  93.  
  94. /* Concerning options: */
  95. int Tabstops[MAXLINE];    /* array of tab stops */
  96. char Unprintable = ' ';    /* char to print for unprintable chars */
  97. int Absnos = NO;    /* use absolute numbers in margin */
  98. int Nchoise = EOS;    /* choice of line number for cont. display */
  99. int Overlay_col = 0;    /* initial cursor column for 'v' command */
  100. int Warncol;        /* where to turn on column warning, set in dosopt() */
  101. int Firstcol = 0;    /* leftmost column to display */
  102. int Indent = 1;        /* indent col; 0=same as previous line */
  103. int Notify = YES;    /* notify user if he has mail in mail file */
  104. int Globals = NO;    /* substitutes in a global don't fail */
  105. int No_hardware;    /* never use hardware insert/delete */
  106.  
  107.  
  108. #ifdef HARD_TERMS
  109. /* Concerning the terminal type */
  110. int Term_type;        /* terminal type */
  111. #endif
  112.  
  113.  
  114. /* Concerning the screen format: */
  115. char Screen_image[MAXROWS][MAXCOLS];
  116. char Msgalloc[MAXCOLS];    /* column allocation of status line */
  117. int Nrows;        /* number of rows on screen */
  118. int Ncols;        /* number of columns on screen */
  119. int Currow;        /* vertical cursor coordinate */
  120. int Curcol;        /* horizontal cursor coordinate */
  121. int Toprow;        /* top row of window field on screen */
  122. int Botrow;        /* bottom row of window field on screen */
  123. int Cmdrow;        /* row number of command line */
  124. int Topln;        /* line number of first line on screen */
  125. int Insert_mode;    /* flag to specify character insertion */
  126. int Invert_case;    /* flag to specify case mapping on input */
  127. int First_affected;    /* number of first line affected by cmd */
  128. int Rel_a;        /* char to use for first alpha line number */
  129. int Rel_z;        /* char to use for last alpha line number */
  130. int Scline[MAXROWS];    /* lines currently on screen (rel to Sctop) */
  131. int Sctop;        /* first line currently on screen */
  132. int Sclen;        /* number of lines currently on screen */
  133. char Blanks[MAXCOLS];    /* all blanks for filling in lines on screen */
  134. char Tobuf[MAXTOBUF];    /* buffer for collecting terminal output */
  135. char *Tobp = Tobuf - 1;    /* pointer to last used part of Tobuf */
  136.  
  137.  
  138. /* Concerning interrupts: */
  139. int Int_caught = 0;    /* caught a SIGINT from user */
  140. int Hup_caught = 0;    /* caught a SIGHUP when phone line dropped */
  141. #ifdef BSD
  142. int Catching_stops;    /* catching or ignoring SIGTSTP's? */
  143. #endif
  144.  
  145. /* Concerning Unix and SWT compatiblity: */
  146. int Unix_mode = YES;    /* behaving like Unix editors? */
  147. char BACKSCAN = '?';    /* back scan character */
  148. char NOTINCCL = '^';    /* class negation character */
  149. char XMARK = '~';    /* global exclude on mark name */
  150. char ESCAPE = '\\';    /* escape character */
  151.  
  152. /* Concering Georgia Tech specific code: */
  153. int At_gtics = NO;    /* are we running at Georgia Tech ICS? */
  154.  
  155. /* Concerning file encryption: */
  156. int Crypting = NO;    /* doing file encryption? */
  157. char Key[KEYSIZE] = "";    /* saved encryption key */
  158.  
  159.  
  160. /* main --- main program for screen editor */
  161.  
  162. main (argc, argv)
  163. int argc;
  164. char *argv[];
  165. {
  166.     char *basename ();
  167.     int int_hdlr (), hup_hdlr ();
  168.     int (*old_int)(), (*old_quit)();
  169. #ifdef BSD
  170.     int stop_hdlr (), (*old_stop)();
  171. #endif
  172.     /* catch quit and hangup signals */
  173.     /*
  174.      * In the terminal driver munging routines, we set Control-P
  175.      * to generate an interrupt, and turn off generating Quits from
  176.      * the terminal.  Now we just ignore them if sent from elsewhere.
  177.      */
  178.  
  179.     signal (SIGHUP, hup_hdlr);
  180.  
  181.     old_int = signal (SIGINT, int_hdlr);
  182.     old_quit = signal (SIGQUIT, SIG_IGN);
  183.  
  184. #ifdef notdef
  185. /*
  186.  * This is commented out so that se can be run from the news
  187.  * software.  Commenting it out will also allow you to put it
  188.  * in the background, which could give you trouble. So beware.
  189.  */
  190.  
  191.     if (old_int == SIG_IGN || old_quit == SIG_IGN)
  192.     {
  193.         /* fired off into the background, refuse to run */
  194.         if (isatty (fileno (stdin)))
  195.         {
  196.             fprintf (stderr, "%s: I refuse to run in the background.\n",
  197.                 basename (argv[0]));
  198.             exit (2);
  199.         }
  200.         /* else
  201.             assume input is a script */
  202.     }
  203. #endif
  204.  
  205. #ifdef BSD
  206.     old_stop = signal (SIGTSTP, stop_hdlr);
  207.  
  208.     if (old_stop == SIG_IGN)    /* running bourne shell */
  209.     {
  210.         signal (SIGTSTP, SIG_IGN);    /* restore it */
  211.         Catching_stops = NO;
  212.     }
  213.     else
  214.         Catching_stops = YES;
  215.         /* running C-shell or BRL sh, catch Control-Z's */
  216. #endif
  217.  
  218.     /* set terminal to no echo, no output processing, break enabled */
  219.     ttyedit ();
  220.  
  221. #ifdef HARD_TERMS
  222.     Tspeed = getspeed (1);        /* speed of stdout */
  223. #endif
  224.  
  225.     initialize (argc, argv);
  226.  
  227.     edit (argc, argv);
  228.  
  229. #ifndef HARD_TERMS
  230.     t_exit ();
  231. #endif
  232.  
  233.     /* reset the terminal mode */
  234.     ttynormal ();
  235. }
  236.  
  237.  
  238. #ifdef HARD_TERMS
  239. /* decode_mnemonic --- decode a terminal type mnemonic */
  240.  
  241. int decode_mnemonic (str)
  242. char str[];
  243. {
  244.     int i;
  245.     int strbsr ();
  246.  
  247.     static struct {
  248.         char *s;
  249.         int t;
  250.     } stab[] = {
  251.         "950",          TVI950,
  252.         "adm31",        ADM31,  
  253.         "adm3a",        ADM3A,  
  254.         "anp",          ANP,    
  255.         "b150",         BEE150,   
  256.         "b200",         BEE200,   
  257.         "cg",           CG,     
  258.         "consul",       ADDS980,
  259.         "esprit",       ESPRIT,
  260.         "fox",          FOX,    
  261.         "gt40",         GT40,   
  262.         "h19",          H19,    
  263.         "haz",          HAZ1510,
  264.         "hp21",         HP21,   
  265.         "hz1510",       HAZ1510,
  266.         "ibm",          IBM,    
  267.         "isc",          ISC8001,
  268.         "netron",       NETRON, 
  269.         "regent",       ADDS100,
  270.         "regent40",     ADDS100,    /* kludge */
  271.         "sbee",         SBEE,   
  272.         "sol",          SOL,    
  273.         "trs80",        TRS80,  
  274.         "ts1",          TS1,
  275.         "tvt",          TVT,    
  276.         "vc4404",       VC4404,
  277.         "vi200",        VI200,
  278.         "vi300",    VI300,
  279.         "vi50",         VI50,
  280.     };
  281.  
  282.     i = strbsr ((char *)stab, sizeof (stab), sizeof (stab[0]), str);
  283.     if (i == EOF)
  284.         return (ERR);
  285.     else
  286.         return (stab[i].t);
  287. }
  288. #endif
  289.  
  290.  
  291.  
  292. /* error --- print error message and die semi-gracefully */
  293.  
  294. error (msg)
  295. char *msg;
  296. {
  297.     /*
  298.      * You might think we want to try and save the buffer,
  299.      * BUT, fatal errors can be caused by buffer problems,
  300.      * which would end up putting us into a non-ending recursion.
  301.      */
  302.  
  303.     ttynormal ();
  304.     fprintf (stderr, "%s\n", msg);
  305.     signal (SIGQUIT, SIG_DFL);    /* restore normal quit handling */
  306.     kill (getpid(), SIGQUIT);    /* dump memory */
  307. }
  308.  
  309.  
  310. /* get_term_type --- force user to divulge terminal type */
  311.  
  312. #ifndef HARD_TERMS
  313. get_term_type ()
  314. {
  315.     int setcaps ();
  316. #else
  317. get_term_type (term_type)
  318. int *term_type;
  319. {
  320.     int decode_mnemonic ();
  321. #endif
  322.  
  323.     char *p;
  324.     char *getenv ();
  325.  
  326.     if ((p = getenv ("TERM")) == NULL)
  327.     {
  328.         ttynormal ();
  329.         fprintf (stderr, "You must set your terminal type with 'TERM=<type>'\n");
  330.         exit (1);
  331.     }
  332.  
  333. #ifdef HARD_TERMS
  334.     if ((*term_type =  decode_mnemonic()) == ERR)
  335. #else
  336.     if (setcaps (p) == ERR)
  337. #endif
  338.     {
  339.         ttynormal ();
  340.         fprintf (stderr, "I'm sorry, but I can't support %s terminals.\n", p);
  341.         exit (1);
  342.     }
  343. }
  344.  
  345.  
  346. /* initialize --- set up global data areas, get terminal type */
  347.  
  348. initialize (argc, argv)
  349. int argc;
  350. char *argv[];
  351. {
  352.     int i, dosopt ();
  353.  
  354. #ifdef HARD_TERMS
  355.     int strcmp ();
  356.     int decode_mnemonic ();
  357.     char lin[MAXLINE];
  358.  
  359.     /* Determine what type of terminal we're on */
  360.     Argno = 1;
  361.     strcpy (lin, &argv[Argno][0]);
  362.     if (Argno < argc && lin[0] == '-' && lin[2] == EOS
  363.         && (lin[1] == 't' || lin[1] == 'T'))
  364.     {
  365.         Argno = 2;
  366.         if (Argno < argc)
  367.         {
  368.             strcpy (lin, argv[Argno]);
  369.             strmap (lin, 'l');
  370.             Term_type = decode_mnemonic (lin);
  371.             if (Term_type == ERR)
  372.                 usage ();
  373.             else
  374.                 Argno++;
  375.         }
  376.         else
  377.             usage ();
  378.     }
  379.     else
  380.         get_term_type (&Term_type);
  381. #else
  382.     Argno = 1;
  383.     get_term_type ();
  384. #endif
  385.  
  386.     /* Initialize the scratch file: */
  387.     mkbuf ();
  388.  
  389.     /* Initialize screen format parameters: */
  390.     setscreen ();
  391.  
  392.     /* Initialize the array of blanks to blanks */
  393.     for (i = 0; i < Ncols; i++)
  394.         Blanks[i] = ' ';
  395.     Blanks[i] = '\0';
  396.  
  397.     if (dosopt ("") == ERR)
  398.         error ("in initialize: can't happen");
  399.  
  400.     return;
  401. }
  402.  
  403.  
  404.  
  405.  
  406. /* intrpt --- see if there has been an interrupt or hangup */
  407.  
  408. int intrpt ()
  409. {
  410.     if (Int_caught)
  411.     {
  412.         Errcode = EBREAK;
  413.         Int_caught = 0;
  414.         return (1);
  415.     }
  416.     else if (Hup_caught)
  417.     {
  418.         Errcode = EHANGUP;
  419.         Hup_caught = 0;
  420.         return 1;
  421.     }
  422.     return (0);
  423. }
  424.  
  425.  
  426. /* int_hdlr --- handle an interrupt signal */
  427.  
  428. int_hdlr ()
  429. {
  430. #ifndef BSD4_2
  431.     signal (SIGINT, int_hdlr);
  432. #endif
  433.     Int_caught = 1;
  434. }
  435.  
  436. /* hup_hdlr --- handle a hangup signal */
  437.  
  438. hup_hdlr ()
  439. {
  440. #ifndef BSD4_2
  441.     signal (SIGHUP, hup_hdlr);
  442.     Hup_caught = 1;
  443. #else
  444.     /* do things different cause of 4.2 (sigh) */
  445.     Hup_caught = 1;
  446.     if (Reading)    /* doing tty i/o, and that is where hup came from */
  447.         hangup ();
  448. #endif
  449. }
  450.  
  451. #ifdef BSD
  452. /* stop_hdlr --- handle the berkeley stop/suspend signal */
  453.  
  454. int stop_hdlr ()
  455. {
  456.     clrscreen ();
  457.     tflush ();
  458.     ttynormal ();
  459. #ifdef BSD4_2
  460.     /* this handler remains in effect, use uncatchable signal */
  461.     kill (getpid(), SIGSTOP);
  462. #else
  463.     /* action was reset to default when we caught it */
  464.     kill (getpid(), SIGTSTP);
  465. #endif
  466.     /*
  467.      * user does a "fg"
  468.      */
  469. #ifndef BSD4_2
  470.     signal (SIGTSTP, stop_hdlr);    /* reset stop catching */
  471. #endif
  472.     ttyedit ();
  473.     restore_screen ();
  474. }
  475. #endif
  476.  
  477.  
  478. /* hangup --- dump contents of edit buffer if SIGHUP occurs */
  479.  
  480. hangup ()
  481. {
  482.     /* close terminal to avoid hanging on any accidental I/O: */
  483.     close (0);
  484.     close (1);
  485.     close (2);
  486.  
  487.     signal (SIGHUP, SIG_IGN);
  488.     signal (SIGINT, SIG_IGN);
  489.     signal (SIGQUIT, SIG_IGN);
  490.     Hup_caught = 0;
  491.     Crypting = NO;        /* force buffer to be clear text */
  492.     dowrit (1, Lastln, "se.hangup", NO, YES, NO);
  493.     clrbuf ();
  494.     exit (1);
  495. }
  496.  
  497.  
  498. /* mswait --- message waiting subroutine */
  499.  
  500. /* if the user wants to be notified, and the mail file is readable, */
  501. /* and there is something in it, then he is given the message. */
  502. /* the om command toggles Notify, controlling notification. */
  503.  
  504. #include <sys/types.h>
  505. #include <sys/stat.h>
  506.  
  507. mswait ()
  508. {
  509.     int access ();
  510.     char *getenv ();
  511.     struct stat buf;
  512.     static char *mbox = NULL;
  513.     static int first = YES;
  514.     static unsigned long mtime = 0L;
  515.  
  516.     if (! Notify)
  517.         return;
  518.  
  519.     if (first) 
  520.     {
  521.         first = NO;
  522.         if ((mbox = getenv ("MAIL")) != NULL && access (mbox, 4) == 0)
  523.         {
  524.             if (stat (mbox, &buf) >= 0)
  525.             {
  526.                 mtime = buf.st_mtime;
  527.                 if (buf.st_size > 0)
  528.                     remark ("You have mail");
  529.             }
  530.         }
  531.     }
  532.     else if (mbox && stat (mbox, &buf) >= 0 && buf.st_mtime > mtime)
  533.     {
  534.         mtime = buf.st_mtime;
  535.         remark ("You have new mail");
  536.         twrite (1, "\007", 1);    /* Bell */
  537.     }
  538. }
  539.  
  540.  
  541. /* printverboseerrormessage --- print verbose error message */
  542.  
  543. printverboseerrormessage ()
  544. {
  545.     switch (Errcode) {
  546.     case EBACKWARD:
  547.         remark ("Line numbers in backward order"); 
  548.         break;
  549.     case ENOPAT:
  550.         remark ("No saved pattern -- sorry"); 
  551.         break;
  552.     case EBADPAT:
  553.         remark ("Bad syntax in pattern"); 
  554.         break;
  555.     case EBADSTR:
  556.         remark ("Bad syntax in string parameter"); 
  557.         break;
  558.     case EBADSUB:
  559.         remark ("Bad syntax in substitution string"); 
  560.         break;
  561.     case ECANTREAD:
  562.         remark ("File is not readable"); 
  563.         break;
  564.     case EEGARB:
  565.         remark ("Garbage after your command"); 
  566.         break;
  567.     case EFILEN:
  568.         remark ("Bad syntax in file name"); 
  569.         break;
  570.     case EBADTABS:
  571.         remark ("Bad tabstop syntax"); 
  572.         break;
  573.     case EINSIDEOUT:
  574.         remark ("Can't move a group into itself"); 
  575.         break;
  576.     case EKNOTFND:
  577.         remark ("No line has that mark name"); 
  578.         break;
  579.     case ELINE1:
  580.         remark (""); 
  581.         break;
  582.     case E2LONG:
  583.         remark ("Resultant line too long to handle"); 
  584.         break;
  585.     case ENOERR:
  586.         remark ("No error to report"); 
  587.         break;
  588.     case ENOLIMBO:
  589.         remark ("No lines in limbo"); 
  590.         break;
  591.     case EODLSSGTR:
  592.         remark ("Expected '<', '>', or nothing after 'od'"); 
  593.         break;
  594.     case EORANGE:
  595.         remark ("Line number out of range"); 
  596.         break;
  597.     case EOWHAT:
  598.         remark ("Can't recognize option"); 
  599.         break;
  600.     case EPNOTFND:
  601.         remark ("No line contains that pattern"); 
  602.         break;
  603.     case ESTUPID:
  604.         remark ("Buffer hasn't been saved"); 
  605.         break;
  606.     case EWHATZAT:
  607.         remark ("No command recognized"); 
  608.         break;
  609.     case EBREAK:
  610.         remark ("You interrupted me"); 
  611.         break;
  612.     case ELINE2:
  613.         remark ("Last line number beyond end of file"); 
  614.         break;
  615.     case ECANTWRITE:
  616.         remark ("File is not writeable"); 
  617.         break;
  618.     case ECANTINJECT:
  619.         remark ("No room for any more lines!"); 
  620.         break;
  621.     case ENOMATCH:
  622.         remark ("No match for pattern"); 
  623.         break;
  624.     case ENOFN:
  625.         remark ("No saved filename"); 
  626.         break;
  627.     case EBADLIST:
  628.         remark ("Bad syntax in character list"); 
  629.         break;
  630.     case ENOLIST:
  631.         remark ("No saved character list -- sorry"); 
  632.         break;
  633.     case ENONSENSE:
  634.         remark ("Unreasonable value"); 
  635.         break;
  636.     case ENOHELP:
  637.         remark ("No help available"); 
  638.         break;
  639.     case EBADLNR:
  640.         remark ("Line numbers not allowed"); 
  641.         break;
  642.     case EFEXISTS:
  643.         remark ("File already exists"); 
  644.         break;
  645.     case EBADCOL:
  646.         remark ("Improper column number specification"); 
  647.         break;
  648.     case ENOLANG:
  649.         remark ("Unknown source language"); 
  650.         break;
  651.     case ETRUNC:
  652.         remark ("Lines were truncated"); 
  653.         break;
  654.     case ENOSHELL:
  655.         remark ("Type control-q to rebuild screen"); 
  656.         break;
  657.     case ECANTFORK:
  658.         remark ("Can't fork --- get help!"); 
  659.         break;
  660.     case ENOSUB:
  661.         remark ("No saved replacement --- sorry"); 
  662.         break;
  663.     case ENOCMD:
  664.         remark ("No saved shell command --- sorry");
  665.         break;
  666.     default:
  667.         remark ("?"); 
  668.         break;
  669.     }
  670.     Errcode = ENOERR;
  671. }
  672.  
  673.  
  674. /* usage --- print usage diagnostic and die */
  675.  
  676. usage ()
  677. {
  678.     ttynormal ();
  679. #ifdef HARD_TERMS
  680.     fprintf (stderr, "Usage: se [-t <terminal>] { <pathname | -<opt> }\n");
  681. #else
  682.     fprintf (stderr, "Usage: se { <pathname> | -<option> }\n");
  683. #endif
  684.     exit (1);
  685. }
  686. SHAR_EOF
  687. fi
  688. echo shar: "extracting 'edit.c'" '(27163 characters)'
  689. if test -f 'edit.c'
  690. then
  691.     echo shar: "will not over-write existing file 'edit.c'"
  692. else
  693. cat << \SHAR_EOF > 'edit.c'
  694. /*
  695. ** edit.c
  696. **
  697. ** editor main routine, plus other routines used a lot.
  698. */
  699.  
  700. #include "se.h"
  701. #include "extern.h"
  702.  
  703. static char Savknm = DEFAULTNAME;    /* saved mark name for < and > */
  704.  
  705. /* edit --- main routine for screen editor */
  706.  
  707. edit (argc, argv)
  708. int argc;
  709. char *argv[];
  710. {
  711.     int cursav, status, len, cursor;
  712.     int ckglob (), docmd (), doglob (), doread ();
  713.     int getlst (), nextln (), prevln ();
  714.     char lin[MAXLINE], term;
  715.  
  716.     watch ();       /* display time of day */
  717.  
  718.     gatech ();    /* check if running at Gatech. do magic stuff if yes */
  719.  
  720.     serc ();    /* execute commands in $HOME/.serc file, if possible */
  721.  
  722.     status = OK;
  723.  
  724.     while (status == OK && Argno < argc)
  725.     {
  726.         strcpy (lin, argv[Argno]);
  727.         loadstr (lin, Argno, POOPCOL, Ncols);
  728.         if (lin[0] == '-')
  729.         {
  730.             len = strlen (lin) + 1;
  731.             lin[len - 1] = '\n';
  732.             lin[len] = EOS;
  733.             len = 0;
  734.             status = doopt (lin, &len);
  735.         }
  736.         else
  737.         {
  738.             dfltsopt (lin);
  739.             status = doread (Lastln, lin, NO);
  740.         }
  741.         Argno++;
  742.     }
  743.  
  744.     if (status == ERR)
  745.     {
  746.         if (Errcode == EHANGUP)
  747.             hangup ();
  748.         printverboseerrormessage ();
  749.     }
  750.     else
  751.         Curln = min (1, Lastln);
  752.  
  753.     Buffer_changed = NO;
  754.     First_affected = 1;     /* maintained by updscreen & commands */
  755.     updscreen ();
  756.  
  757.     if (status != ERR)    /* leave offending file name or option */
  758.         lin[0] = EOS;
  759.     cursor = 0;
  760.  
  761.     /* main command loop */
  762.     do {
  763.         intrpt ();    /* discard pending breaks (interrupts) */
  764.         if (Lost_lines > GARB_THRESHOLD
  765.             && (Lastln + Limcnt) / Lost_lines <= GARB_FACTOR)
  766.             garbage_collect ();
  767.  
  768.         mswait ();    /* check for pending messages */
  769.         Cmdrow = Botrow + 1;    /* reset the command line location */
  770.         prompt ("cmd>");
  771.         getcmd (lin, 0, &cursor, &term);
  772.         remark ("");    /* clear out any error messages */
  773.  
  774.         while (term == CURSOR_UP || term == CURSOR_DOWN
  775.             || term == CURSOR_SAME)
  776.         {
  777.             switch (term) {
  778.             case CURSOR_UP:
  779.                 if (Curln > 1)
  780.                     Curln--;
  781.                 else
  782.                     Curln = Lastln;
  783.                 break;
  784.  
  785.             case CURSOR_DOWN:
  786.                 if (Curln < Lastln)
  787.                     Curln++;
  788.                 else
  789.                     Curln = min (1, Lastln);
  790.                 break;
  791.             }
  792.             adjust_window (Curln, Curln);
  793.             updscreen ();
  794.             getcmd (lin, 0, &cursor, &term);
  795.         }
  796.  
  797.         prompt ("");    /* remove prompt */
  798.  
  799.         cursav = Curln;        /* remember it in case of an error */
  800.         Errcode = EEGARB;    /* default error code for garbage at end */
  801.  
  802.         len = 0;
  803.         if (getlst (lin, &len, &status) == OK)
  804.         {
  805.             if (ckglob (lin, &len, &status) == OK)
  806.                 doglob (lin, &len, &cursav, &status);
  807.             else if (status != ERR)
  808.                 docmd (lin, len, NO, &status);
  809.         }
  810.         if (status == ERR)
  811.         {
  812.             if (Errcode == EHANGUP)
  813.                 hangup ();
  814.             printverboseerrormessage ();
  815.             Curln = min (cursav, Lastln);
  816.         }
  817.         else if (term != FUNNY)
  818.         {
  819.             cursor = 0;
  820.             lin[0] = EOS;
  821.         }
  822.  
  823.         adjust_window (Curln, Curln);
  824.         updscreen ();
  825.  
  826.     } while (status != EOF);
  827.  
  828.     clrscreen ();
  829.     clrbuf ();
  830.     tflush ();
  831.  
  832.     return;
  833. }
  834.  
  835.  
  836. /* getlst --- collect line numbers (if any) at lin[*i], increment i */
  837.  
  838. int getlst (lin, i, status)
  839. char lin[];
  840. int *i, *status;
  841. {
  842.     int num;
  843.     int getone ();
  844.  
  845.     Line2 = 0;
  846.     for (Nlines = 0; getone (lin, i, &num, status) == OK; )
  847.     {
  848.         Line1 = Line2;
  849.         Line2 = num;
  850.         Nlines++;
  851.         if (lin[*i] != ',' && lin[*i] != ';')
  852.             break;
  853.         if (lin[*i] == ';')
  854.             Curln = num;
  855.         (*i)++;
  856.     }
  857.  
  858.     if (Nlines > 2)
  859.         Nlines = 2;
  860.  
  861.     if (Nlines <= 1)
  862.         Line1 = Line2;
  863.  
  864.     if (Line1 > Line2)
  865.     {
  866.         *status = ERR;
  867.         Errcode = EBACKWARD;
  868.     }
  869.  
  870.     if (*status != ERR)
  871.         *status = OK;
  872.  
  873.     return (*status);
  874. }
  875.  
  876.  
  877. /* getnum --- convert one term to line number */
  878.  
  879. int getnum (lin, i, pnum, status)
  880. char lin[];
  881. register int *i, *pnum, *status;
  882. {
  883.     int j, ret;
  884.     int ctoi (), optpat (), ptscan (), knscan (), getkn ();
  885.     int k;
  886.  
  887.     ret = OK;
  888.     SKIPBL (lin, *i);
  889.     if (lin[*i] >= Rel_a && lin[*i] <= Rel_z && Absnos == NO)
  890.         *pnum = Topln - Toprow + lin[*i] - Rel_a;
  891.     else if (lin[*i] == CURLINE)
  892.         *pnum = Curln;
  893.     else if (lin[*i] == PREVLINE || lin[*i] == PREVLINE2)
  894.         *pnum = Curln - 1;
  895.     else if (lin[*i] == LASTLINE)
  896.         *pnum = Lastln;
  897.     else if (lin[*i] == SCAN || lin[*i] == BACKSCAN)
  898.     {
  899.         int missing_delim = YES;
  900.  
  901.         /* see if trailing delim supplied, since command can follow pattern */
  902.         for (k = *i + 1; lin[k] != EOS; k++)
  903.             if (lin[k] == ESCAPE)
  904.                 k++;    /* skip esc, loop will skip escaped char */
  905.             else if (lin[k] == lin[*i])
  906.             {
  907.                 missing_delim = NO;
  908.                 break;
  909.             }
  910.             /* else
  911.                 continue */
  912.         
  913.         if (missing_delim == YES)
  914.         {
  915.             for (; lin[k] != EOS; k++)
  916.                 ;
  917.             k--;        /* k now at newline */
  918.  
  919.             /* supply trailing delim */
  920.             lin[k] = lin[*i];
  921.             lin[++k] = '\n';
  922.             lin[++k] = EOS;
  923.             Peekc = SKIP_RIGHT;
  924.         }
  925.  
  926.         if (optpat (lin, i) == ERR)
  927.             ret = ERR;
  928.         else if (lin[*i] == SCAN)
  929.             ret = ptscan (FORWARD, pnum);
  930.         else 
  931.             ret = ptscan (BACKWARD, pnum);
  932.     }
  933.     else if (lin[*i] == SEARCH || lin[*i] == BACKSEARCH)
  934.     {
  935.         j = *i;
  936.         (*i)++;
  937.         if (getkn (lin, i, &Savknm, Savknm) == ERR)
  938.             ret = ERR;
  939.         else if (lin[j] == SEARCH)
  940.             ret = knscan (FORWARD, pnum);
  941.         else
  942.             ret = knscan (BACKWARD, pnum);
  943.         (*i)--;
  944.     }
  945.     else if (isdigit (lin[*i]))
  946.     {
  947.         *pnum = ctoi (lin, i);
  948.         (*i)--;
  949.     }
  950.     else if (lin[*i] == TOPLINE)
  951.         *pnum = Topln;
  952.     else
  953.         ret = EOF;
  954.  
  955.     if (ret == OK)
  956.         (*i)++;
  957.     *status = ret;
  958.     return (ret);
  959. }
  960.  
  961.  
  962. /* getone --- evaluate one line number expression */
  963.  
  964. int getone (lin, i, num, status)
  965. char lin[];
  966. register int *i, *num, *status;
  967. {
  968.     int pnum, ret;
  969.     int getnum ();
  970.     char porm;    /* "plus or minus" (sic) */
  971.  
  972.     ret = EOF;    /* assume we won't find anything for now */
  973.     *num = 0;
  974.  
  975.     if (getnum (lin, i, num, status) == OK)        /* first term */
  976.     {
  977.         ret = OK;    /* to indicate we've seen something */
  978.         do {            /* + or - terms */
  979.             porm = EOS;
  980.             SKIPBL (lin, *i);
  981.             if (lin[*i] == '-' || lin[*i] == '+')
  982.             {
  983.                 porm = lin[*i];
  984.                 (*i)++;
  985.             }
  986.             if (getnum (lin, i, &pnum, status) == OK)
  987.                 if (porm == '-')
  988.                     *num -= pnum;
  989.                 else
  990.                     *num += pnum;
  991.             if (*status == EOF && porm != EOS)    /* trailing + or - */
  992.                 *status = ERR;
  993.         } while (*status == OK);
  994.     }
  995.  
  996.     if (*num < 0 || *num > Lastln)    /* make sure number is in range */
  997.     {
  998.         *status = ERR;
  999.         Errcode = EORANGE;
  1000.     }
  1001.  
  1002.     if (*status == ERR)
  1003.         ret = ERR;
  1004.     else
  1005.         *status = ret;
  1006.  
  1007.     return (ret);
  1008. }
  1009.  
  1010.  
  1011. #ifndef OLD_SCRATCH
  1012. #ifndef OLD_GLOB
  1013. static int special_casing = NO;
  1014. #endif
  1015. #endif
  1016.  
  1017. /* ckglob --- if global prefix, mark lines to be affected */
  1018.  
  1019. int ckglob (lin, i, status)
  1020. char lin[];
  1021. int *i, *status;
  1022. {
  1023.     register int line, tmp;
  1024.     int usepat, usemark;
  1025.     int defalt (), match (), optpat (), getkn ();
  1026.     register LINEDESC *k;
  1027.     LINEDESC *gettxt (), *getind ();
  1028. #ifndef OLD_SCRATCH
  1029. #ifndef OLD_GLOB
  1030.     char start_line = Unix_mode ? '^' : '%';
  1031. #endif
  1032. #endif
  1033.  
  1034.     *status = OK;
  1035.     usepat = EOF;
  1036.     usemark = EOF;
  1037.  
  1038. #ifndef OLD_SCRATCH
  1039. #ifndef OLD_GLOB
  1040.     if (    /* g/^/m0  or g/$/m0 -- special case the pathological */
  1041.         /* cases in order to save time */
  1042.         (lin[*i] == GLOBAL || lin[*i] == UCGLOBAL)
  1043.         && (lin[*i + 1] == lin[*i + 3])
  1044.         && (lin[*i + 2] == start_line || lin[*i + 2] == '$')
  1045.         && (lin[*i + 4] == MOVECOM || lin[*i + 4] == UCMOVECOM)
  1046.         && (lin[*i + 5] == '0' && lin[*i + 6] == '\n')   )
  1047.     {
  1048.         special_casing = YES;
  1049.         remark ("GLOB");
  1050.         return (OK);
  1051.     }
  1052. #endif
  1053. #endif
  1054.     if (lin[*i] == GMARK || lin[*i] == XMARK)    /* global markname prefix? */
  1055.     {
  1056.         if (lin[*i] == GMARK)    /* tag lines with the specified markname */
  1057.             usemark = YES;
  1058.         else            /* tag lines without the specified markname */
  1059.             usemark = NO;
  1060.         (*i)++;
  1061.         *status = getkn (lin, i, &Savknm, Savknm);
  1062.     }
  1063.  
  1064.     if (*status == OK)    /* check for a pattern prefix too */
  1065.     {
  1066.         if (lin[*i] == GLOBAL || lin[*i] == UCGLOBAL)
  1067.             usepat = YES;
  1068.  
  1069.         if (lin[*i] == EXCLUDE || lin[*i] == UCEXCLUDE)
  1070.             usepat = NO;
  1071.  
  1072.         if (usepat != EOF)
  1073.         {
  1074.             (*i)++;
  1075.             if (optpat (lin, i) == ERR)
  1076.                 *status = ERR;
  1077.             else
  1078.                 (*i)++;
  1079.         }
  1080.     }
  1081.  
  1082.     if (*status == OK && usepat == EOF && usemark == EOF)
  1083.         *status = EOF;
  1084.     else if (*status == OK)
  1085.         defalt (1, Lastln);
  1086.  
  1087.     if (*status == OK)    /* no errors so far, safe to proceed */
  1088.     {
  1089.         remark ("GLOB");
  1090.  
  1091.         k = Line0;      /* unmark all lines preceeding range */
  1092.         for (line = 0; line < Line1; line++)
  1093.         {
  1094.             k -> Globmark = NO;
  1095.             k = NEXTLINE(k);
  1096.         }
  1097.  
  1098.         for (; line <= Line2; line++)    /* mark lines in range */
  1099.         {
  1100.             if (intrpt ())
  1101.             {
  1102.                 *status = ERR;
  1103.                 return (*status);
  1104.             }
  1105.             tmp = NO;
  1106.             if (usemark == EOF
  1107.                 || usemark == YES && k -> Markname == Savknm
  1108.                 || usemark == NO && k -> Markname != Savknm)
  1109.             {
  1110.                 if (usepat == EOF)    /* no global pattern to look for */
  1111.                     tmp = YES;
  1112.                 else    /* there is also a pattern to look for */
  1113.                 {
  1114.                     gtxt (k);
  1115.                     if (match (Txt, Pat) == usepat)
  1116.                         tmp = YES;
  1117.                 }
  1118.             }
  1119.  
  1120.             k -> Globmark = tmp;
  1121.  
  1122.             k = NEXTLINE(k);
  1123.         }
  1124.  
  1125. #ifdef OLD_SCRATCH
  1126.         /* mark remaining lines */
  1127.         for (; k != Line0; k = k -> Nextline)
  1128.             k -> Globmark = NO;
  1129. #else
  1130.         /* mark remaining lines */
  1131.         for (; line <= Lastln; line++)
  1132.         {
  1133.             k -> Globmark = NO;
  1134.             k = NEXTLINE (k);
  1135.         }
  1136. #endif
  1137.  
  1138.         remark ("");
  1139.     }
  1140.  
  1141.     return (*status);
  1142. }
  1143.  
  1144.  
  1145. /* doglob --- do command at lin[i] on all marked lines */
  1146.  
  1147. int doglob (lin, i, cursav, status)
  1148. char lin[];
  1149. int *i, *cursav, *status;
  1150. {
  1151.     register int istart, line;
  1152.     int docmd (), getlst (), nextln ();
  1153.     register LINEDESC *k;
  1154.     LINEDESC *getind ();
  1155.  
  1156. #ifndef OLD_SCRATCH
  1157. #ifndef OLD_GLOB
  1158.     if (special_casing)
  1159.     {
  1160.         /*
  1161.         remark ("Warp 7, Captain!");
  1162.         */
  1163.         /* not on the screen too long anyway */
  1164.         reverse (1, Lastln);
  1165.         Curln = Lastln;
  1166.         special_casing = NO;
  1167.         Buffer_changed = YES;
  1168.         First_affected = min (1, First_affected);
  1169.         remark ("");
  1170.         adjust_window (Curln, Curln);
  1171.         updscreen ();
  1172.         return (OK);
  1173.     }
  1174. #endif
  1175. #endif
  1176.     *status = OK;
  1177.     istart = *i;
  1178.     k = Line0;
  1179.     line = 0;
  1180.  
  1181.     do {
  1182.         line++;
  1183.         k = NEXTLINE(k);
  1184.         if (k -> Globmark == YES)    /* line is marked */
  1185.         {
  1186.             k -> Globmark = NO;    /* unmark the line */
  1187.             Curln = line;
  1188.             *cursav = Curln;    /* remember where we are */
  1189.             *i = istart;
  1190.             if (getlst (lin, i, status) == OK)
  1191.                 docmd (lin, *i, YES, status);
  1192.             line = 0;        /* lines may have been moved */
  1193.             k = Line0;
  1194.         }
  1195.         if (intrpt ())
  1196.             *status = ERR;
  1197.     } while (line <= Lastln && *status == OK);
  1198.  
  1199.     return (*status);
  1200. }
  1201.  
  1202.  
  1203. /* ckchar --- look for ch or altch on lin at i, set flag if found */
  1204.  
  1205. int ckchar (ch, altch, lin, i, flag, status)
  1206. char ch, altch, lin[];
  1207. int *i, *flag, *status;
  1208. {
  1209.  
  1210.     if (lin[*i] == ch || lin[*i] == altch)
  1211.     {
  1212.         (*i)++;
  1213.         *flag = YES;
  1214.     }
  1215.     else
  1216.         *flag = NO;
  1217.  
  1218.     *status = OK;
  1219.     return (OK);
  1220. }
  1221.  
  1222.  
  1223. /* ckp --- check for "p" after command */
  1224.  
  1225. int ckp (lin, i, pflag, status)
  1226. char lin[];
  1227. int i, *pflag, *status;
  1228. {
  1229.  
  1230.     if (lin[i] == PRINT || lin[i] == UCPRINT)
  1231.     {
  1232.         i++;
  1233.         *pflag = YES;
  1234.     }
  1235.     else
  1236.         *pflag = NO;
  1237.  
  1238.     if (lin[i] == '\n')
  1239.         *status = OK;
  1240.     else
  1241.         *status = ERR;
  1242.  
  1243.     return (*status);
  1244. }
  1245.  
  1246.  
  1247. /* ckupd --- make sure it is ok to destroy the buffer */
  1248.  
  1249. int ckupd (lin, i, cmd, status)
  1250. char lin[], cmd;
  1251. int *i, *status;
  1252. {
  1253.     int flag;
  1254.     int ckchar ();
  1255.  
  1256.     *status = ckchar (ANYWAY, ANYWAY, lin, i, &flag, status);
  1257.     if (flag == NO && Buffer_changed == YES && Probation != cmd)
  1258.     {
  1259.         *status = ERR;
  1260.         Errcode = ESTUPID;
  1261.         Probation = cmd;        /* if same command is repeated, */
  1262.     }                       /* we'll keep quiet */
  1263.  
  1264.     return (*status);
  1265. }
  1266.  
  1267.  
  1268. /* defalt --- set defaulted line numbers */
  1269.  
  1270. defalt (def1, def2)
  1271. int def1, def2;
  1272. {
  1273.  
  1274.     if (Nlines == 0)        /* no line numbers supplied, use defaults */
  1275.     {
  1276.         Line1 = def1;
  1277.         Line2 = def2;
  1278.     }
  1279.  
  1280.     return;
  1281. }
  1282.  
  1283.  
  1284. /* getfn --- get file name from lin[i]... */
  1285.  
  1286. int getfn (lin, i, file)
  1287. char lin[], file[];
  1288. int i;
  1289. {
  1290.     int j, k, ret;
  1291.  
  1292.     ret = ERR;
  1293.     if (lin[i + 1] == ' ')
  1294.     {
  1295.         j = i + 2;      /* get new file name */
  1296.         SKIPBL (lin, j);
  1297.         for (k = 0; lin[j] != NEWLINE; k++, j++)
  1298.             file[k] = lin[j];
  1299.         file[k] = EOS;
  1300.         if (k > 0)
  1301.             ret = OK;
  1302.     }
  1303.     else if (lin[i + 1] == '\n' && Savfil[0] != EOS)
  1304.     {
  1305.         strcpy (file, Savfil);    /* or old name */
  1306.         ret = OK;
  1307.     }
  1308.     else 
  1309.         if (lin[i + 1] == '\n')
  1310.             Errcode = ENOFN;
  1311.         else
  1312.             Errcode = EFILEN;
  1313.  
  1314.     if (ret == OK && Savfil[1] == EOS)
  1315.     {
  1316.         strcpy (Savfil, file);        /* save if no old one */
  1317.         mesg (Savfil, FILE_MSG);
  1318.     }
  1319.  
  1320.     return (ret);
  1321. }
  1322.  
  1323.  
  1324. /* getkn --- get mark name from lin[i], increment i */
  1325.  
  1326. int getkn (lin, i, kname, dfltnm)
  1327. char lin[], *kname, dfltnm;
  1328. int *i;
  1329. {
  1330.  
  1331.     if (lin[*i] == '\n' || lin[*i] == EOS)
  1332.     {
  1333.         *kname = dfltnm;
  1334.         return (EOF);
  1335.     }
  1336.  
  1337.     *kname = lin[*i];
  1338.     (*i)++;
  1339.     return (OK);
  1340. }
  1341.  
  1342.  
  1343. /* getrange --- get 'from' range for tlit command */
  1344.  
  1345. int getrange (array, k, set, size, allbut)
  1346. char array[], set[];
  1347. int *k, size, *allbut;
  1348. {
  1349.     int i, j;
  1350.     int addset ();
  1351.  
  1352.     Errcode = EBADLIST;    /* preset error code */
  1353.  
  1354.     i = *k + 1;
  1355.     if (array[i] == NOTINCCL)    /* check for negated character class */
  1356.     {
  1357.         *allbut = YES;
  1358.         i++;
  1359.     }
  1360.     else
  1361.         *allbut = NO;
  1362.  
  1363.     j = 0;
  1364.     filset (array[*k], array, &i, set, &j, size);
  1365.     if (array[i] != array[*k])
  1366.     {
  1367.         set[0] = EOS;
  1368.         return (ERR);
  1369.     }
  1370.     if (set[0] == EOS)
  1371.     {
  1372.         Errcode = ENOLIST;
  1373.         return (ERR);
  1374.     }
  1375.     if (j > 0 && addset (EOS, set, &j, size) == NO)
  1376.     {
  1377.         set[0] = EOS;
  1378.         return (ERR);
  1379.     }
  1380.  
  1381.     *k = i;
  1382.     Errcode = EEGARB;
  1383.  
  1384.     return (OK);
  1385. }
  1386.  
  1387.  
  1388. /* getrhs --- get substitution string for 's' command */
  1389.  
  1390. int getrhs (lin, i, sub, gflag)
  1391. char lin[], sub[];
  1392. int *i, *gflag;
  1393. {
  1394.     static char Subs[MAXPAT] = "";    /* saved replacement pattern */
  1395.     int j, maksub ();
  1396.     char saved_sub = Unix_mode ? '%' : '&';
  1397.     /* saved replacement pattern char */
  1398.  
  1399.  
  1400.     Errcode = EBADSUB;
  1401.  
  1402.     if (lin[*i] == EOS)    /* missing the middle delimeter */
  1403.         return (ERR);
  1404.  
  1405.     if (lin[*i + 1] == saved_sub && (lin[*i + 2] == lin[*i]
  1406.                     || lin[*i + 2] == '\n'))
  1407.     {
  1408.     /*
  1409.      * s//%/ --- should mean do the same thing as I did last time, even
  1410.      * s//&/ --- if I deleted something. So we comment out these lines.
  1411.      *
  1412.         if (Subs[0] == EOS)
  1413.         {
  1414.             Errcode = ENOSUB;
  1415.             return (ERR);
  1416.         }
  1417.      */
  1418.         strcpy (sub, Subs);
  1419.         *i += 2;
  1420.         if (lin[*i] == '\n')
  1421.         {
  1422.             /* fix it up for pattern matching routines */
  1423.             lin[*i] = lin[*i - 2];
  1424.             lin[*i + 1] = '\n';
  1425.             lin[*i + 2] = EOS;
  1426.             Peekc = SKIP_RIGHT;
  1427.         }
  1428.     }
  1429.     else        /* not using saved substitution pattern */
  1430.     {
  1431.         if (lin[*i + 1] == '\n')
  1432.         {
  1433.             /* missing the trailing delimiter */
  1434.             /* pattern was empty */
  1435.             lin[*i + 1] = lin[*i];    /* supply missing delimiter */
  1436.             lin[*i + 2] = '\n';
  1437.             lin[*i + 3] = EOS;
  1438.             Peekc = SKIP_RIGHT;
  1439.             /* return (ERR);    /* this is the original action */
  1440.         }
  1441.         else
  1442.         {
  1443.             /* stuff in pattern, check end of line */
  1444.             for (j = *i; lin[j] != EOS; j++)
  1445.                 ;
  1446.             j -= 2;        /* j now points to char before '\n' */
  1447.  
  1448.             if (lin[j] == 'p' || lin[j] == 'P')
  1449.             {
  1450.                 --j;
  1451.                 if (lin[j] == GLOBAL || lin[j] == UCGLOBAL)
  1452.                 {
  1453.                     if (j >= *i + 1 && lin[j-1] == lin[*i]
  1454.                         && (lin[j-2] != ESCAPE
  1455.                             || lin[j-3] == ESCAPE))
  1456.                         ;     /* leave alone */
  1457.                     else
  1458.                     {
  1459.                         /* \<delim>gp\n is pattern */
  1460.                         /* supply trailing delim */
  1461.                         j +=  2;    /* j at \n */
  1462.                         lin[j] = lin[*i];
  1463.                         lin[++j] = '\n';
  1464.                         lin[++j] = EOS;
  1465.                         Peekc = SKIP_RIGHT;
  1466.                     }
  1467.                 }
  1468.                 else if (j >= *i + 1 && lin[j] == lin[*i] &&
  1469.                         (lin[j-1] != ESCAPE
  1470.                          || lin[j-2] == ESCAPE))
  1471.                     ;    /* leave alone */
  1472.                 else
  1473.                 {
  1474.                     /* \<delim>p\n is pattern */
  1475.                     /* supply trailing delim */
  1476.                     j += 2;
  1477.                     lin[j] = lin[*i];
  1478.                     lin[++j] = '\n';
  1479.                     lin[++j] = EOS;
  1480.                     Peekc = SKIP_RIGHT;
  1481.                 }
  1482.             }
  1483.             else if (lin[j] == GLOBAL || lin[j] == UCGLOBAL)
  1484.             {
  1485.                 --j;
  1486.                 if (j >= *i + 1 && lin[j] == lin[*i] &&
  1487.                     (lin[j-1] != ESCAPE
  1488.                      || lin[j-2] == ESCAPE))
  1489.                     ;     /* leave alone */
  1490.                 else
  1491.                 {
  1492.                     /* \<delim>g\n is pattern */
  1493.                     /* supply trailing delim */
  1494.                     j +=  2;    /* j at \n */
  1495.                     lin[j] = lin[*i];
  1496.                     lin[++j] = '\n';
  1497.                     lin[++j] = EOS;
  1498.                     Peekc = SKIP_RIGHT;
  1499.                 }
  1500.             }
  1501.             else if ((lin[j] != lin[*i]) ||
  1502.                 (lin[j] == lin[*i] &&
  1503.                 lin[j-1] == ESCAPE && lin[j-2] != ESCAPE))
  1504.             {
  1505.                 /* simply missing trailing delimeter */
  1506.                 /* supply it */
  1507.                 j++;        /* j at \n */
  1508.                 lin[j] = lin[*i];
  1509.                 lin[++j] = '\n';
  1510.                 lin[++j] = EOS;
  1511.                 Peekc = SKIP_RIGHT;
  1512.             }
  1513.             /* else
  1514.                 unescaped delim is there,
  1515.                 leave well enough alone */
  1516.         }
  1517.  
  1518.         if ((*i = maksub (lin, *i + 1, lin[*i], sub)) == ERR)
  1519.             return (ERR);
  1520.  
  1521.         strcpy (Subs, sub);    /* save pattern for later */
  1522.     }
  1523.  
  1524.     if (lin[*i + 1] == GLOBAL || lin[*i + 1] == UCGLOBAL)
  1525.     {
  1526.         (*i)++;
  1527.         *gflag = YES;
  1528.     }
  1529.     else
  1530.         *gflag = NO;
  1531.  
  1532.     Errcode = EEGARB;    /* the default */
  1533.  
  1534.     return (OK);
  1535.  
  1536. }
  1537.  
  1538.  
  1539. /* getstr --- get string from lin at i, copy to dst, bump i */
  1540.  
  1541. /*
  1542. ** NOTE: this routine only called for doing the join command.
  1543. ** therefore, don't do anything else with it.
  1544. */
  1545.  
  1546. int getstr (lin, i, dst, maxdst)
  1547. char lin[], dst[];
  1548. int *i, maxdst;
  1549. {
  1550.     char delim;
  1551.     char esc ();
  1552.     int j, k, d;
  1553.  
  1554.     j = *i;
  1555.     Errcode = EBADSTR;
  1556.  
  1557.     delim = lin[j];
  1558.  
  1559.     if (delim == '\n')
  1560.     {
  1561.         lin[j] = '/';
  1562.         lin[++j] = ' ';        /* join with a single blank */
  1563.         lin[++j] = '/';
  1564.         lin[++j] = '\n';
  1565.         lin[++j] = EOS;
  1566.         j = *i;
  1567.         delim = lin[j];
  1568.         Peekc = SKIP_RIGHT;
  1569.         /* now fall thru */
  1570.  
  1571.         /* return (ERR);    /* old way */
  1572.     }
  1573.     else if ((delim == 'p' || delim == 'P') && lin[j + 1] == '\n')    /* jp */
  1574.     {
  1575.         lin[j] = '/';
  1576.         lin[++j] = ' ';        /* join with a single blank */
  1577.         lin[++j] = '/';
  1578.         lin[++j] = delim;    /* 'p' or 'P' */
  1579.         lin[++j] = '\n';
  1580.         lin[++j] = EOS;
  1581.         j = *i;
  1582.         delim = lin[j];
  1583.         Peekc = SKIP_RIGHT;
  1584.         /* now fall thru */
  1585.     }
  1586.  
  1587.     if (lin[j + 1] == '\n')        /* command was 'j/' */
  1588.     {
  1589.         dst[0] = EOS;
  1590.         Errcode = ENOERR;
  1591.         return (OK);
  1592.         /* return (ERR);    /* old way */
  1593.     }
  1594.  
  1595.     /*
  1596.      * otherwise, stuff there in the string, try to allow for
  1597.      * a missing final delimiter.
  1598.      */
  1599.  
  1600.     for (k = j + 1; lin[k] != '\n'; k++)
  1601.         ;    /* find end */
  1602.     
  1603.     k--;    /* now points to char before newline */
  1604.  
  1605.     if (lin[k] == 'p' || lin[k] == 'P')
  1606.     {
  1607.         k--;
  1608.         if (lin[k] == delim &&
  1609.             (lin[k-1] != ESCAPE || lin[k-2] == ESCAPE))
  1610.             ;    /* it's fine, leave it alone */
  1611.         else
  1612.         {
  1613.             /* ESCAPE delim p NEWLINE is the join string */
  1614.             /* supply trailing delimiter. */
  1615.             k += 2;
  1616.             lin[k] = delim;
  1617.             lin[++k] = '\n';
  1618.             lin[++k] = EOS;
  1619.             Peekc = SKIP_RIGHT;
  1620.         }
  1621.     }
  1622.     else if (lin[k] != delim || (lin[k-1] == ESCAPE && lin[k-2] != ESCAPE))
  1623.     {
  1624.         /* no delim and no p, or last char is escaped delim */
  1625.         k++;
  1626.         lin[k] = delim;
  1627.         lin[++k] = '\n';
  1628.         lin[++k] = EOS;
  1629.         Peekc = SKIP_RIGHT;
  1630.     }
  1631.     /* else
  1632.         delim is there
  1633.         leave well enough alone */
  1634.  
  1635.     /* code to actually do the join */
  1636.  
  1637.     for (k = j + 1; lin[k] != delim; k++)    /* find end */
  1638.     {
  1639.         if (lin[k] == '\n' || lin[k] == EOS)
  1640.             if (delim == ' ')
  1641.                 break;
  1642.             else
  1643.                 return (ERR);
  1644.         esc (lin, &k);
  1645.     }
  1646.     if (k - j > maxdst)
  1647.         return (ERR);
  1648.  
  1649.     for (d = 0, j++; j < k; d++, j++)
  1650.         dst[d] = esc (lin, &j);
  1651.     dst[d] = EOS;
  1652.  
  1653.     *i = j;
  1654.     Errcode = EEGARB;    /* the default */
  1655.  
  1656.     return (OK);
  1657. }
  1658.  
  1659.  
  1660. /* getwrd --- get next word from line at i; increment i */
  1661.  
  1662. int getwrd (line, i, word, size)
  1663. char line[], word[];
  1664. int *i, size;
  1665. {
  1666.     int j;
  1667.  
  1668.     SKIPBL (line, *i);
  1669.     j = 0;
  1670.     while (line[*i] != ' ' && line[*i] != '\n' && line[*i] != EOS)
  1671.     {
  1672.         if (j < size - 1)
  1673.         {
  1674.             word[j] = line[*i];
  1675.             j++;
  1676.         }
  1677.         (*i)++;
  1678.     }
  1679.     word[j] = EOS;
  1680.  
  1681.     return (j);
  1682. }
  1683.  
  1684.  
  1685. /* knscan --- scan for a line with a given mark name */
  1686.  
  1687. int knscan (way, num)
  1688. int way, *num;
  1689. {
  1690.     int nextln ();
  1691.     LINEDESC *k;
  1692.     LINEDESC *getind ();
  1693.  
  1694.     *num = Curln;
  1695.     k = getind (*num);
  1696.     do {
  1697.         bump (num, &k, way);
  1698.         if (k -> Markname == Savknm)
  1699.             return (OK);
  1700.     } while (*num != Curln && ! intrpt ());
  1701.  
  1702.     if (Errcode = EEGARB)
  1703.         Errcode = EKNOTFND;
  1704.     return (ERR);
  1705.  
  1706. }
  1707.  
  1708.  
  1709. /* makset --- make set from array[k] in set */
  1710.  
  1711. int makset (array, k, set, size)
  1712. char array[], set[];
  1713. int *k, size;
  1714. {
  1715.     static char Tset[MAXPAT] = "";    /* saved translit dest range */
  1716.     int i, j;
  1717.     int l;
  1718.     int addset ();
  1719.     char saved_sub = Unix_mode ? '%' : '&';
  1720.  
  1721.     Errcode = EBADLIST;
  1722.  
  1723.     /*
  1724.      * try to allow missing delimiter for translit command.
  1725.      */
  1726.     
  1727.     if (array[*k] == EOS)
  1728.         return (ERR);
  1729.  
  1730.     if (array[*k + 1] == saved_sub && (array[*k + 2] == array[*k]
  1731.                        || array[*k + 2] == '\n'))
  1732.     {
  1733.         strcpy (set, Tset);
  1734.         *k += 2;
  1735.         if (array[*k] == '\n')
  1736.         {
  1737.             /* fix it up for rest of the routines */
  1738.             array[*k] = array[*k - 2];
  1739.             array[*k+ 1] = '\n';
  1740.             array[*k+ 2] = EOS;
  1741.         }
  1742.         Peekc = SKIP_RIGHT;
  1743.     }
  1744.     else
  1745.     {
  1746.     
  1747.         for (l = *k; array[l] != EOS; l++)
  1748.             ;
  1749.         l -= 2;        /* l now points to char before '\n' */
  1750.     
  1751.         if (l == *k)    /* "y/.../\n" */
  1752.         {
  1753.             array[*k + 1] = array[*k];    /* add delimiter */
  1754.             array[*k + 2] = '\n';
  1755.             array[*k + 3] = EOS;
  1756.             Peekc = SKIP_RIGHT;
  1757.         }
  1758.         else if (array[l] == 'p' || array[l] == 'P')
  1759.         {
  1760.             --l;
  1761.             if (l >= *k + 1 && array[l] == array[*k] &&
  1762.                 (array[l-1] != ESCAPE || array[l-2] == ESCAPE))
  1763.                 ;    /* leave alone */
  1764.             else
  1765.             {
  1766.                 /* \<delim>p\n is set */
  1767.                 /* supply trailing delim */
  1768.                 l += 2;
  1769.                 array[l] = array[*k];
  1770.                 array[++l] = '\n';
  1771.                 array[++l] = EOS;
  1772.                 Peekc = SKIP_RIGHT;
  1773.             }
  1774.         }
  1775.         else if (array[l] != array[*k]    /* no delim, and no p */
  1776.             || (array[l-1] == ESCAPE    /* or last char is escaped delim */
  1777.             && array[l-2] != ESCAPE))
  1778.         {
  1779.             /* simply missing trailing delimeter */
  1780.             /* supply it */
  1781.             l++;        /* l now at \n */
  1782.             array[l] = array[*k];
  1783.             array[++l] = '\n';
  1784.             array[++l] = EOS;
  1785.             Peekc = SKIP_RIGHT;
  1786.         }
  1787.         /* else
  1788.             delim is there,
  1789.             leave well enough alone */
  1790.  
  1791.         j = 0;
  1792.         i = *k + 1;
  1793.         filset (array[*k], array, &i, set, &j, size);
  1794.  
  1795.         if (array[i] != array[*k])
  1796.             return (ERR);
  1797.  
  1798.         if (addset (EOS, set, &j, size) == NO)
  1799.             return (ERR);
  1800.  
  1801.         strcpy (Tset, set);    /* save for later */
  1802.         *k = i;
  1803.  
  1804.     }
  1805.  
  1806.     Errcode = EEGARB;
  1807.  
  1808.     return (OK);
  1809. }
  1810.  
  1811.  
  1812. /* optpat --- make pattern specified at lin[i] */
  1813.  
  1814. int optpat (lin, i)
  1815. char lin[];
  1816. int *i;
  1817. {
  1818.     int makpat ();
  1819.  
  1820.     if (lin[*i] == EOS)
  1821.         *i = ERR;
  1822.     else if (lin[*i + 1] == EOS)
  1823.         *i = ERR;
  1824.     else if (lin[*i + 1] == lin[*i])    /* repeated delimiter */
  1825.         (*i)++;        /* leave existing pattern alone */
  1826.     else
  1827.         *i = makpat (lin, *i + 1, lin[*i], Pat);
  1828.  
  1829.     if (Pat [0] == EOS)
  1830.     {
  1831.         Errcode = ENOPAT;
  1832.         return (ERR);
  1833.     }
  1834.     if (*i == ERR)
  1835.     {
  1836.         Pat[0] = EOS;
  1837.         Errcode = EBADPAT;
  1838.         return (ERR);
  1839.     }
  1840.     return (OK);
  1841. }
  1842.  
  1843.  
  1844. /* ptscan --- scan for next occurrence of pattern */
  1845.  
  1846. int ptscan (way, num)
  1847. int way, *num;
  1848. {
  1849.     LINEDESC *getind ();
  1850.     LINEDESC *k;
  1851.     int match ();
  1852.  
  1853.     *num = Curln;
  1854.     k = getind (*num);
  1855.     do {
  1856.         bump (num, &k, way);
  1857.         gtxt (k);
  1858.         if (match (Txt, Pat) == YES)
  1859.             return (OK);
  1860.     } while (*num != Curln && ! intrpt ());
  1861.  
  1862.     if (Errcode == EEGARB)
  1863.         Errcode = EPNOTFND;
  1864.  
  1865.     return (ERR);
  1866. }
  1867.  
  1868.  
  1869. /* settab --- set tab stops */
  1870.  
  1871. int settab (str)
  1872. char str[];
  1873. {
  1874.     int i, j, n, maxstop, last, inc, ret;
  1875.     int ctoi ();
  1876.  
  1877.     for (i = 0; i < MAXLINE; i++)   /* clear all tab stops */
  1878.         Tabstops[i] = NO;
  1879.  
  1880.     ret = OK;
  1881.     maxstop = 0;
  1882.     last = 1;
  1883.  
  1884.     i = 0;
  1885.     SKIPBL (str, i);
  1886.     while (str[i] != EOS && str[i] != '\n')
  1887.     {
  1888.         if (str[i] == '+')      /* increment */
  1889.         {
  1890.             i++;
  1891.             inc = YES;
  1892.         }
  1893.         else
  1894.             inc = NO;
  1895.  
  1896.         n = ctoi (str, &i);
  1897.  
  1898.         if (n <= 0 || n >= MAXLINE)
  1899.         {
  1900.             ret = ERR;
  1901.             Errcode = ENONSENSE;
  1902.             break;
  1903.         }
  1904.  
  1905.         if (str[i] != ' ' && str[i] != '+' &&
  1906.             str[i] != '\n' && str[i] != EOS)
  1907.         {
  1908.             ret = ERR;
  1909.             Errcode = EBADTABS;
  1910.             break;
  1911.         }
  1912.  
  1913.         if (inc == YES)
  1914.         {
  1915.             for (j = last + n; j < MAXLINE; j += n)
  1916.             {
  1917.                 Tabstops[j - 1] = YES;
  1918.                 maxstop = max (j, maxstop);
  1919.             }
  1920.         }
  1921.         else
  1922.         {
  1923.             Tabstops[n - 1] = YES;
  1924.             last = n;
  1925.             maxstop = max (n, maxstop);
  1926.         }
  1927.         SKIPBL (str, i);
  1928.     }       /* while ... */
  1929.  
  1930.     if (ret == ERR)
  1931.         maxstop = 0;
  1932.  
  1933.     if (maxstop == 0)       /* no tab stops specified, use defaults */
  1934.     {
  1935.         for (i = 4; i < MAXLINE - 1; i += 4)
  1936.             Tabstops[i] = YES;
  1937.         maxstop = i - 4 + 1;
  1938.     }
  1939.  
  1940.     Tabstops[0] = YES;      /* always set to YES */
  1941.  
  1942.     for (i = maxstop; i < MAXLINE; i++)
  1943.         Tabstops[i] = YES;
  1944.  
  1945.     return (ret);
  1946. }
  1947.  
  1948. /* gatech --- see if se is running at Ga. Tech. */
  1949.  
  1950. /*
  1951. ** if se is running at gatech, for the sake of naive users,
  1952. ** come up in SWT compatibility mode.  Personally, I would
  1953. ** rather not do this, but, the sophisticated users can use
  1954. ** a .serc file to turn on unix compatibility.  If not at
  1955. ** gatech, default behaviour is Unix compatibility.....
  1956. **
  1957. ** set the global flag, so that we can do some neat stuff in do_shell()
  1958. */
  1959.  
  1960. static gatech ()
  1961. {
  1962.     int len;
  1963.     int i;
  1964.     extern char *sysname ();    /* will tell us where we are */
  1965.  
  1966.     /* add system names here as Gatech gets more UNIX machines */
  1967.     /* the names MUST be in sorted order */
  1968.     /* also include the stupid gt-* names */
  1969.     static char *stab[] = {
  1970.         "cirrus",
  1971.         "gatech",
  1972.         "gt-cirrus",
  1973.         "gt-nimbus",
  1974.         "gt-stratus",
  1975.         "nimbus",
  1976.         "stratus"
  1977.         };
  1978.  
  1979.     i = strbsr ((char *) stab, sizeof (stab), sizeof (stab[0]), sysname());
  1980.  
  1981.     if (i != EOF)
  1982.     {
  1983.         len = 0;
  1984.         doopt ("ops\n", &len);    /* turn on SWT compatibility */
  1985.                     /* will put 'SWT' in status line */
  1986.         At_gtics = YES;
  1987. #ifdef LOG_USAGE
  1988.         log ();        /* log se usage statistics */
  1989. #endif
  1990.     }
  1991.     else
  1992.     {
  1993.         mesg ("UNIX", MODE_MSG);
  1994.         At_gtics = NO;
  1995.     }
  1996. }
  1997.  
  1998. /* serc --- read in $HOME/.serc and execute the commands in it, if possible. */
  1999.  
  2000. /*
  2001.  * note that se's special control characters are NOT processed,
  2002.  * and therefore should NOT be used in one's .serc file.
  2003.  */
  2004.  
  2005. static serc ()
  2006. {
  2007.     char file[MAXLINE];
  2008.     char lin[MAXLINE];
  2009.     char *expand_env ();
  2010.     FILE *fp;
  2011.     int status = ENOERR;
  2012.     int len, cursav;
  2013.  
  2014.     strcpy (file, expand_env ("$HOME/.serc"));
  2015.  
  2016.     if ((fp = fopen (file, "r")) == NULL)
  2017.         return;
  2018.     
  2019.     while (fgets (lin, sizeof lin, fp) != NULL && status != EOF /*??*/)
  2020.     {
  2021.         if (lin[0] == '#' || lin[0] == '\n')
  2022.             continue;    /* comment in .serc file */
  2023.  
  2024.         /* most of this code stolen from edit() */
  2025.         len = 0;
  2026.         cursav = Curln;
  2027.         if (getlst (lin, &len, &status) == OK)
  2028.         {
  2029.             if (ckglob (lin, &len, &status) == OK)
  2030.                 doglob (lin, &len, &cursav, &status);
  2031.             else if (status != ERR)
  2032.                 docmd (lin, len, NO, &status);
  2033.         }
  2034.         if (status == ERR)
  2035.         {
  2036.             if (Errcode == EHANGUP)
  2037.                 hangup ();
  2038.             Curln = min (cursav, Lastln);
  2039.         }
  2040.     }
  2041.     fclose (fp);
  2042. }
  2043.  
  2044. #ifdef LOG_USAGE
  2045.  
  2046. /* log -- log se usage, iff at Georgia Tech */
  2047.  
  2048.  
  2049. static log ()
  2050. {
  2051.     static char logfile[] = "/usr/tmp/se.log";    /* a public file */
  2052.     char logname[MAXLINE], tod[26];        /* tod => time of day */
  2053.     long clock;
  2054.     FILE *fp;
  2055.     char *ctime ();
  2056.     long time ();
  2057.     int old_umask;
  2058. #ifdef BSD
  2059.     char *getlogin ();
  2060. #else
  2061.     char *cuserid ();
  2062. #endif
  2063.  
  2064.     if (! At_gtics)
  2065.         return;
  2066.  
  2067.     /* get the login name */
  2068. #ifdef BSD
  2069.     strcpy (logname, getlogin ());
  2070. #else
  2071.     cuserid (logname);
  2072. #endif
  2073.  
  2074.     time (&clock);
  2075.     strcpy (tod, ctime (&clock));    /* see the manual on ctime(3C)  */
  2076.     tod[24] = EOS;            /* delete the '\n' at the end */
  2077.  
  2078.     old_umask = umask (0);        /* allow writes for everyone */
  2079.                     /* when first call creates the file */
  2080.  
  2081.     if ((fp = fopen (logfile, "a")) != NULL)
  2082.     {
  2083.         /* all ok, write out statistics */
  2084.         fprintf (fp, "%s used se on %s.\n", logname, tod);
  2085.         fclose (fp);
  2086.     }
  2087.     /* else
  2088.         don't do anything */
  2089.  
  2090.     umask (old_umask);
  2091.  
  2092. }
  2093. #endif
  2094.  
  2095. /* sysname --- return a string telling us who we are */
  2096.  
  2097. #ifdef USG
  2098. #include <sys/utsname.h>    /* stuff to find out who we are */
  2099. #endif
  2100.  
  2101. char *sysname ()
  2102. {
  2103.     int i, j, k;
  2104.     char c;
  2105.     static char buf[MAXLINE] = "";
  2106.     FILE *fp;
  2107.  
  2108. #ifdef USG    /* System V */
  2109.     static struct utsname whoarewe;
  2110.  
  2111.     uname (& whoarewe);
  2112.     return (whoarewe.sysname);
  2113. #else
  2114. #ifdef BSD4_2    /* Berkeley 4.2 */
  2115.     if (buf[0] != EOS)
  2116.         return (buf);
  2117.  
  2118.     j = sizeof (buf);
  2119.     k = gethostname (buf, & j);
  2120.     if (k != 0)
  2121.         return ("unknown");
  2122.     else
  2123.         return (buf);
  2124. #else        /* Berkeley 4.1 */
  2125.     if (buf[0] != EOS)
  2126.         return (buf);
  2127.  
  2128.     if ((fp = fopen ("/usr/include/whoami.h", "r")) == NULL)
  2129.         return ("unknown");
  2130.     else
  2131.     {
  2132.         auto char *cp;
  2133.         /*
  2134.          * file should contain a single line:
  2135.          * #define sysname "......"
  2136.          */
  2137.         while ((c = getc (fp)) != '"' && c != EOF)
  2138.             ;
  2139.         if (c == EOF)
  2140.             cp = "unknown";
  2141.         else
  2142.         {
  2143.             for (i = 0; (c = getc (fp)) != '"' && c != EOF; i++)
  2144.                 buf[i] = c;
  2145.             buf[i] = EOS;
  2146.             if (c == EOF && i == 0)
  2147.                 cp = "unknown";
  2148.             else
  2149.                 cp = buf;
  2150.         }
  2151.         fclose (fp);
  2152.         return (cp);
  2153.     }
  2154. #endif
  2155. #endif
  2156. }
  2157. SHAR_EOF
  2158. fi
  2159. echo shar: "extracting 'scratch.c'" '(11282 characters)'
  2160. if test -f 'scratch.c'
  2161. then
  2162.     echo shar: "will not over-write existing file 'scratch.c'"
  2163. else
  2164. cat << \SHAR_EOF > 'scratch.c'
  2165. /*
  2166. ** scratch.c
  2167. **
  2168. ** scratch file handling for se screen editor.
  2169. **
  2170. ** If OLD_SCRATCH is defined, then this file will contain the
  2171. ** original scratch file handling, based on linked lists,
  2172. ** from the ratfor version of Software Tools.  This method is
  2173. ** real good at moving lines around, but is poor for finding lines.
  2174. **
  2175. ** If OLD_SCRATCH is not defined, which is the default, this file will use
  2176. ** the line handling methodology presented in Software Tools In Pascal,
  2177. ** *without* changing the way any of the routines are called.
  2178. **
  2179. ** Bascially, the lines are always kept in order in the Buf array.
  2180. ** Thus, lines 1 through 5 are in Buf[1] through Buf[5]. blkmove() and
  2181. ** reverse() do the work of moving lines around in the buffer. The alloc()
  2182. ** routine, therefore, always allocates the first empty slot, which will be
  2183. ** at Lastln + 1, if there is room.
  2184. **
  2185. ** Deleted lines are kept at the end of the buffer. Limbo points to the first
  2186. ** line in the group of lines which were last deleted, or else Limbo == NOMORE.
  2187. **
  2188. ** It is a very good idea to read the chapters on editing in BOTH editions of
  2189. ** Software Tools, before trying to muck with this. It also helps to be a
  2190. ** little bit off the wall....
  2191. **
  2192. ** In fact, I would go as far as saying, "If you touch this, it will break.
  2193. ** It is held together with chewing gum, scotch tape, and bobby pins."
  2194. ** (Of course, you could always use OLD_SCRATCH, which definitely works.
  2195. ** It just increases the size of the editor.)  So much for the old
  2196. ** "Just replace the implementation of the underlying primitives..."
  2197. */
  2198.  
  2199. #include "se.h"
  2200. #include "extern.h"
  2201.  
  2202. /* alloc --- allocate space for a new pointer block */
  2203.  
  2204.  
  2205. static LINEDESC *alloc (ptr)
  2206. register LINEDESC **ptr;
  2207. {
  2208. #ifdef OLD_SCRATCH    /* old way */
  2209.     if (Free == NOMORE)   /* no free list, expand into unallocated space */
  2210.     {
  2211.         if (Lastbf - Buf + BUFENT <= MAXBUF)   /* see if there's room */
  2212.         {
  2213.             *ptr = Lastbf;
  2214.             Lastbf += BUFENT;
  2215.         }
  2216.         else
  2217.             *ptr = NOMORE;        /* out of pointer space */
  2218.     }
  2219.     else    /* remove a block from free list */
  2220.     {
  2221.         *ptr = Free;
  2222.         Free = Free->Prevline;
  2223.     }
  2224. #else    /* new way */
  2225.     int limbo_index = Limbo - Buf;    /* use indices instead of pointers */
  2226.         /* N.B.: this statement is meaningless if Limbo == NOMORE */
  2227.         /* but if so, we don't use limbo_index anyway */
  2228.  
  2229.     if (Limbo == NOMORE)
  2230.         if (Lastln < (MAXBUF - 1) - 1)    /* dumb zero based indexing! */
  2231.             *ptr = &Buf[Lastln + 1];
  2232.         else
  2233.             *ptr = NOMORE;
  2234.     else if (limbo_index - Lastln > 1)
  2235.         *ptr = &Buf[Lastln + 1];
  2236.     else
  2237.         *ptr = NOMORE;
  2238. #endif
  2239.  
  2240.     return (*ptr);
  2241. }
  2242.  
  2243.  
  2244. /* bump --- advance line number and corresponding index simultaneously */
  2245.  
  2246. bump (line, ix, way)
  2247. int *line, way;
  2248. LINEDESC **ix;
  2249. {
  2250.     if (way == FORWARD)    /* increment line number */
  2251.     {
  2252. #ifdef OLD_SCRATCH
  2253.         *ix = (*ix)->Nextline;
  2254.         if (*ix == Line0)
  2255.             *line = 0;
  2256.         else
  2257.             (*line)++;
  2258. #else
  2259.         (*ix)++;
  2260.         if (*ix == &Buf[Lastln+1])
  2261.         {
  2262.             *line = 0;
  2263.             *ix = Line0;
  2264.         }
  2265.         else
  2266.             (*line)++;
  2267. #endif
  2268.     }
  2269.     else    /* decrement line number */
  2270.     {
  2271.         if (*ix == Line0)
  2272.             *line = Lastln;
  2273.         else
  2274.             (*line)--;
  2275. #ifdef OLD_SCRATCH
  2276.         *ix = (*ix)->Prevline;
  2277. #else
  2278.         if (*ix == Line0)
  2279.             *ix = &Buf[Lastln];
  2280.         else
  2281.             (*ix)--;
  2282. #endif
  2283.     }
  2284. }
  2285.  
  2286.  
  2287.  
  2288. /* closef --- close a file */
  2289.  
  2290. static closef (fd)
  2291. filedes fd;
  2292. {
  2293.     close (fd);
  2294. }
  2295.  
  2296.  
  2297.  
  2298.  
  2299. /* clrbuf --- purge scratch file */
  2300.  
  2301. clrbuf ()
  2302. {
  2303.  
  2304.     if (Lastln > 0)
  2305.         svdel (1, Lastln);
  2306.  
  2307.     closef (Scr);
  2308.     unlink (Scrname);
  2309. }
  2310.  
  2311.  
  2312.  
  2313. /* garbage_collect --- compress scratch file */
  2314.  
  2315. garbage_collect ()
  2316. {
  2317.     char new_name [MAXLINE];
  2318.     register int i, new_scrend;
  2319.     int new_fd;
  2320.     register LINEDESC *p;
  2321.  
  2322.     makscr (&new_fd, new_name);
  2323.     remark ("collecting garbage");
  2324.     new_scrend = 0;
  2325. #ifdef OLD_SCRATCH
  2326.     for (p = Limbo, i = 1; i <= Limcnt; p = p->Nextline, i++)
  2327. #else
  2328.     for (p = Limbo, i = 1; i <= Limcnt; p++, i++)
  2329. #endif
  2330.     {
  2331.         gtxt (p);
  2332.         seekf ((long) new_scrend * 8, new_fd);
  2333.         writef (Txt, (int) p->Lineleng, new_fd);
  2334.         p->Seekaddr = new_scrend;
  2335.         new_scrend += (p->Lineleng + 7) / 8;
  2336.     }
  2337. #ifdef OLD_SCRATCH
  2338.     for (p = Line0, i = 0; i <= Lastln; p = p->Nextline, i++)
  2339. #else
  2340.     for (p = Line0, i = 0; i <= Lastln; p++, i++)
  2341. #endif
  2342.     {
  2343.         gtxt (p);
  2344.         seekf ((long) new_scrend * 8, new_fd);
  2345.         writef (Txt, (int) p->Lineleng, new_fd);
  2346.         p->Seekaddr = new_scrend;
  2347.         new_scrend += (p->Lineleng + 7) / 8;
  2348.     }
  2349.  
  2350.     closef (Scr);
  2351.     unlink (Scrname);
  2352.  
  2353.     Scr = new_fd;
  2354.     sprintf (Scrname, "%s", new_name);
  2355.     Scrend = new_scrend;
  2356.     Lost_lines = 0;
  2357.  
  2358.     remark ("");
  2359. }
  2360.  
  2361.  
  2362.  
  2363. /* gettxt --- locate text for line, copy to txt */
  2364.  
  2365. LINEDESC *gettxt (line)
  2366. int line;
  2367. {
  2368.     register LINEDESC *k;
  2369.     LINEDESC *getind ();
  2370.  
  2371.     k = getind (line);
  2372.     gtxt (k);
  2373.  
  2374.     return (k);
  2375. }
  2376.  
  2377.  
  2378.  
  2379. /* gtxt --- retrieve a line from the scratch file */
  2380.  
  2381. gtxt (ptr)
  2382. register LINEDESC *ptr;
  2383. {
  2384.     int readf ();
  2385.  
  2386.     seekf ((long) ptr->Seekaddr * 8, Scr); /* position to start of file */
  2387.     /*
  2388.      * rounded Seekaddr to 8 byte sections, giving larger
  2389.      * buffer space for text (*8)
  2390.      */
  2391.  
  2392.     return (readf (Txt, (int) ptr->Lineleng, Scr) - 1);
  2393. }
  2394.  
  2395.  
  2396.  
  2397. /* inject --- insert a new line after curln */
  2398.  
  2399. inject (lin)
  2400. register char lin [];
  2401. {
  2402.     register int i;
  2403.     int maklin ();
  2404.     register LINEDESC *k1, *k2;
  2405.     LINEDESC *k3;
  2406.     LINEDESC *getind ();
  2407.  
  2408.     for (i = 0; lin [i] != EOS; )
  2409.     {
  2410.         i = maklin (lin, i, &k3);       /* create a single line */
  2411.         if (i == ERR)
  2412.         {
  2413.             Errcode = ECANTINJECT;
  2414.             return (ERR);
  2415.         }
  2416. #ifdef OLD_SCRATCH
  2417.         k1 = getind (Curln);            /* get pointer to curln */
  2418.         k2 = k1-> Nextline;             /* get pointer to nextln */
  2419.         relink (k1, k3, k3, k2);        /* set pointers of new line */
  2420.         relink (k3, k2, k1, k3);        /* set pointers of prev, next */
  2421.         svins (Curln, 1);
  2422.         Lastln++;        /* update Lastln */
  2423. #else
  2424.         Lastln++;        /* update Lastln */
  2425.         blkmove (Lastln, Lastln, Curln);
  2426.         svins (Curln, 1);
  2427. #endif
  2428.         Curln++;        /* update Curln */
  2429.     }
  2430.     return (OK);
  2431. }
  2432.  
  2433.  
  2434.  
  2435. /* maklin --- construct a new line, add to scratch file */
  2436.  
  2437. maklin (lin, i, newind)
  2438. register char lin [];
  2439. register int i;
  2440. LINEDESC **newind;
  2441. {
  2442.  
  2443.     char text [MAXLINE];
  2444.     register int l, n;
  2445.     LINEDESC *ptr;
  2446.     LINEDESC *alloc ();
  2447.  
  2448.     if (alloc (&ptr) == NOMORE)     /* get space for pointer block */
  2449.         return (ERR);
  2450.  
  2451.     for (n = i; lin [n] != EOS; n++)    /* find end of line */
  2452.         if (lin [n] == '\n')
  2453.         {
  2454.             n++;
  2455.             break;
  2456.         }
  2457.  
  2458.     if (n - i >= MAXLINE )  /* can't handle more than MAXLINE chars/line */
  2459.         n = i + MAXLINE - 1;
  2460.     l = n - i + 1;          /* length of new line (including EOS) */
  2461.  
  2462.     move_ (&lin [i], text, l);      /* move new line into text */
  2463.     text [l - 1] = EOS;             /* add EOS */
  2464.  
  2465.     ptr->Seekaddr = Scrend; /* will be added to end of scratch file */
  2466.     ptr->Lineleng = l;      /* line length including EOS */
  2467.     ptr->Globmark = NO;     /* not marked for Global command */
  2468.     ptr->Markname = DEFAULTNAME;    /* give it default mark name */
  2469.  
  2470.     seekf ((long) Scrend * 8, Scr); /* go to end of scratch file */
  2471.     writef (text, l, Scr);          /* write line on scratch file */
  2472.     Scrend += (l + 7) / 8;          /* update end-of-file pointer */
  2473.  
  2474.     Buffer_changed = YES;
  2475.  
  2476.     *newind = ptr;                  /* return index of new line */
  2477.     return (n);                     /* return next char of interest in lin */
  2478. }
  2479.  
  2480.  
  2481.  
  2482. /* makscr --- create a new scratch file */
  2483.  
  2484. makscr (fd, str)
  2485. register filedes *fd;
  2486. register char str[];
  2487. {
  2488.     register int i;
  2489.  
  2490.     for (i = 0; i <= 9; i++)
  2491.     {
  2492.         sprintf (str, "/usr/tmp/se%d.%d", getpid(), i);
  2493.         /* create str name in /usr/tmp */
  2494.         if ((*fd = open (str, 0)) < 0)
  2495.         {
  2496.             /* if the file is not there, close it and create it */
  2497.             close (*fd);
  2498.             if ((*fd = creat (str, 0700)) > 0)
  2499.             {
  2500.                 close (*fd);
  2501.                 if ((*fd = open (str, 2)) > 0)
  2502.                     return;
  2503.             }
  2504.         }
  2505.         else
  2506.             close (*fd);
  2507.  
  2508.     }
  2509.     error ("can't create scratch file");
  2510. }
  2511.  
  2512.  
  2513.  
  2514. /* nextln --- get line after "line" */
  2515.  
  2516. nextln (line)
  2517. int line;
  2518. {
  2519.     register int ret;
  2520.  
  2521.     ret = line + 1;
  2522.     if (ret > Lastln)
  2523.         ret = 0;
  2524.  
  2525.     return (ret);
  2526. }
  2527.  
  2528.  
  2529.  
  2530. /* prevln --- get line before "line" */
  2531.  
  2532. prevln (line)
  2533. int line;
  2534. {
  2535.     register int ret;
  2536.  
  2537.     ret = line - 1;
  2538.     if (ret < 0)
  2539.         ret = Lastln;
  2540.  
  2541.     return (ret);
  2542. }
  2543.  
  2544.  
  2545.  
  2546. /* readf --- read count words from fd into buf */
  2547.  
  2548. readf (buf, count, fd)
  2549. char buf [];
  2550. int  count, fd;
  2551. {
  2552.     register int ret;
  2553.  
  2554.     ret = read (fd, buf, count);
  2555.     if (ret != count)
  2556.         error ("Fatal scratch file read error");
  2557.  
  2558.     return (ret);
  2559. }
  2560.  
  2561.  
  2562. #ifdef OLD_SCRATCH
  2563. /* relink --- rewrite two half links */
  2564.  
  2565. relink (a, x, y, b)
  2566. LINEDESC *a, *b, *x, *y;
  2567. {
  2568.     x->Prevline = a;
  2569.     y->Nextline = b;
  2570. }
  2571. #endif
  2572.  
  2573.  
  2574.  
  2575. /* seekf --- position file open on fd to pos */
  2576.  
  2577. static seekf (pos, fd)
  2578. long pos;
  2579. filedes fd;
  2580. {
  2581.     register long ret;
  2582.     long lseek ();
  2583.  
  2584.     ret = lseek (fd, pos, 0);               /* abs seek */
  2585.     if (ret != pos)
  2586.         error ("Fatal scratch file seek error");
  2587.     return (OK);
  2588. }
  2589.  
  2590.  
  2591.  
  2592. /* mkbuf --- create scratch file, initialize line 0 */
  2593.  
  2594. mkbuf ()
  2595. {
  2596.     LINEDESC *p;
  2597.  
  2598.     makscr (&Scr, Scrname);    /* create a scratch file */
  2599.     Scrend = 0;        /* initially empty */
  2600.  
  2601.     Curln = 0;
  2602. #ifdef OLD_SCRATCH
  2603.     Lastln = 0;
  2604. #else
  2605.     Lastln = -1;        /* alloc depends on this... */
  2606. #endif
  2607.  
  2608. #ifdef OLD_SCRATCH
  2609.     Lastbf = &Buf[0];       /* next word available for allocation ?? */
  2610.     Free = NOMORE;          /* free list initially empty */
  2611. #endif
  2612.     Limbo = NOMORE;         /* no lines in limbo */
  2613.     Limcnt = 0;
  2614.     Lost_lines = 0;         /* no garbage in scratch file yet */
  2615.  
  2616.     maklin ("", 0, &p);     /* create an empty line */
  2617. #ifdef OLD_SCRATCH
  2618.     relink (p, p, p, p);    /* establish initial linked list */
  2619. #endif
  2620.     p->Markname = EOS;    /* give it an illegal mark name */
  2621.     Line0 = p;              /* henceforth and forevermore */
  2622.  
  2623. #ifndef OLD_SCRATCH
  2624.     Lastln = 0;
  2625. #endif
  2626. }
  2627.  
  2628.  
  2629.  
  2630. /* sp_inject --- special inject for reading files */
  2631.  
  2632. LINEDESC *sp_inject (lin, len, line)
  2633. char lin[];
  2634. int  len;
  2635. LINEDESC *line;
  2636. {
  2637.     register LINEDESC *k, *ret;
  2638.     LINEDESC *ptr;
  2639.     LINEDESC *alloc ();
  2640.  
  2641.     ret = alloc (&ptr);
  2642.     if (ptr == NOMORE)
  2643.     {
  2644.         Errcode = ECANTINJECT;
  2645.         return (ret);
  2646.     }
  2647.  
  2648.     ptr->Seekaddr = Scrend;
  2649.     ptr->Lineleng = len + 1;
  2650.     ptr->Globmark = NO;
  2651.     ptr->Markname = DEFAULTNAME;
  2652.  
  2653.     seekf ((long) Scrend * 8, Scr);
  2654.     writef (lin, len + 1, Scr);
  2655.     Scrend += ((len + 1) + 7) / 8;          /* fudge for larger buffer */
  2656.     Lastln++;
  2657.  
  2658.     Buffer_changed = YES;
  2659.  
  2660. #ifdef OLD_SCRATCH
  2661.     k = line->Nextline;
  2662.     relink (line, ptr, ptr, k);
  2663.     relink (ptr, k, line, ptr);
  2664. #else
  2665.     /*
  2666.      * this part dependant on the fact that we set
  2667.      * Curln = line in the routine do_read.
  2668.      */
  2669.     blkmove (ptr - Buf, ptr - Buf, Curln);    /* need line no's */
  2670.     Curln++;
  2671. #endif
  2672.  
  2673.     return (ret);
  2674. }
  2675.  
  2676.  
  2677.  
  2678. /* writef --- write count words from buf onto fd */
  2679.  
  2680. writef (buf, count, fd)
  2681. char buf[];
  2682. int  count;
  2683. filedes fd;
  2684. {
  2685.     register int ret;
  2686.  
  2687.     ret = write (fd, buf, count);
  2688.     if (ret != count)
  2689.         error ("Fatal scratch file write error");
  2690.     return (ret);
  2691. }
  2692.  
  2693.  
  2694.  
  2695. /* getind --- locate line index in buffer */
  2696.  
  2697. LINEDESC *getind (line)
  2698. register int line;
  2699. {
  2700. #ifdef OLD_SCRATCH
  2701.     register LINEDESC *k;
  2702.  
  2703.     k = Line0;
  2704.     line++;
  2705.     while (--line)
  2706.         k = k->Nextline;
  2707.  
  2708.     return (k);
  2709. #else
  2710.     return (&Buf[line]);
  2711. #endif
  2712. }
  2713.  
  2714. #ifndef OLD_SCRATCH
  2715.  
  2716. /* blkmove -- use SWT in Pascal line handling */
  2717.  
  2718. blkmove (n1, n2, n3)    /* move block of lines n1..n2 to after n3 */
  2719. int n1, n2, n3;
  2720. {
  2721.     if (n3 < n1 -1)
  2722.     {
  2723.         reverse (n3 + 1, n1 - 1);
  2724.         reverse (n1, n2);
  2725.         reverse (n3 + 1, n2);
  2726.     }
  2727.     else if (n3 > n2)
  2728.     {
  2729.         reverse (n1, n2);
  2730.         reverse (n2 + 1, n3);
  2731.         reverse (n1, n3);
  2732.     }
  2733. }
  2734.  
  2735. /* reverse -- reverse buf[n1]..buf[n2] */
  2736.  
  2737. reverse (n1, n2)
  2738. register int n1, n2;
  2739. {
  2740.     LINEDESC temp;
  2741.  
  2742.     while (n1 < n2)
  2743.     {
  2744.         temp = Buf[n1];
  2745.         Buf[n1] = Buf[n2];
  2746.         Buf[n2] = temp;
  2747.         n1++;
  2748.         n2--;
  2749.     }
  2750. }
  2751. #endif
  2752. SHAR_EOF
  2753. fi
  2754. exit 0
  2755. #    End of shell archive
  2756.  
  2757.